/*-------------------------------------------------------------------------
 *
 * timestamp.h
 *	  时间戳和间隔的类型定义及相关宏。
 *
 * 注意：此文件必须在前端和后端上下文中都可包含。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/datatype/timestamp.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef DATATYPE_TIMESTAMP_H
#define DATATYPE_TIMESTAMP_H

/*
 * 时间戳表示绝对时间。
 *
 * 间隔表示增量时间。分别跟踪月（和年）、天，
 * 以及小时/分钟/秒，因为所经过的时间跨度
 * 直到相对于绝对时间实例化后才是未知的。
 *
 * 请注意，Postgres使用“时间间隔”来指代一个有界的区间，
 * 由开始和结束时间组成，而不是时间跨度 - thomas 97/03/20
 *
 * 时间戳以及间隔的h/m/s字段以
 * int64值（单位为微秒）存储。（很久以前它们是
 * 以秒为单位的双精度值。）
 *
 * TimeOffset和fsec_t是临时变量的便利类型定义。
 * 不要在磁盘上存储的值中使用fsec_t。
 * 此外，fsec_t仅用于*分数*秒；如果您需要存储的值可能是很多秒，请注意溢出。
 */

typedef int64 Timestamp;
typedef int64 TimestampTz;
typedef int64 TimeOffset;
typedef int32 fsec_t;			/* 分数秒（以微秒为单位） */


/*
 * 类型间隔的存储格式。
 */
typedef struct
{
	TimeOffset	time;			/* 所有时间单位，除了天、月和
								 * 年 */
	int32		day;			/* 天，经过时间对齐之后 */
	int32		month;			/* 月份和年份，经过对齐时间 */
} Interval;

/*
 * 表示分解间隔的数据结构。
 *
 * 出于历史原因，这是基于时间戳的结构pg_tm建模的。
 * 不同于时间戳的情况，对于月份或年份，没有魔法解释
 * 是必要的：它们只是零或者不是。请注意，字段
 * 可以是负数；但是，由于在从结构Interval转换时进行的
 * 除法，只有tm_mday可能是INT_MIN。这一点很重要，
 * 因为我们可能需要在某些代码路径中否定这些值。
 */
struct pg_itm
{
	int			tm_usec;
	int			tm_sec;
	int			tm_min;
	int64		tm_hour;		/* 需要变宽 */
	int			tm_mday;
	int			tm_mon;
	int			tm_year;
};

/*
 * 解码间隔的数据结构。我们可以直接使用结构pg_itm，
 * 但如果这样做，tm_usec需要64位的要求将传播到
 * 并不真正需要的地方。此外，省略在解码过程中
 * 不使用的字段似乎是一个良好的防错措施。
 */
struct pg_itm_in
{
	int64		tm_usec;		/* 需要变宽 */
	int			tm_mday;
	int			tm_mon;
	int			tm_year;
};


/* 这些数据类型的“精度”选项（typmod）的限制 */
#define MAX_TIMESTAMP_PRECISION 6
#define MAX_INTERVAL_PRECISION 6

/*
 * 四舍五入到MAX_TIMESTAMP_PRECISION位小数。
 * 注意：这也用于对间隔进行四舍五入。
 */
#define TS_PREC_INV 1000000.0
#define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)


/*
 * 与日期时间相关计算的各种常量
 */

#define DAYS_PER_YEAR	365.25	/* 假设每四年一个闰年 */
#define MONTHS_PER_YEAR 12
/*
 *	每月天数非常不精确。更准确的值是
 *	365.2425/12 = 30.436875，或“30天10:29:06”。现在我们只
 *	返回一个整数的天数，但将来或许我们也应该
 *	返回一个“时间”值以供使用。ISO 8601 建议
 *	30天。
 */
#define DAYS_PER_MONTH	30		/* 假设每月正好30天 */
#define HOURS_PER_DAY	24		/* 假设没有夏令时变化 */

/*
 *	这没有调整不均匀的夏令时间隔或闰
 *	秒，并且粗略估计闰年。每年天数的更准确值
 *	是365.2422。
 */
#define SECS_PER_YEAR	(36525 * 864)	/* 避免浮点计算 */
#define SECS_PER_DAY	86400
#define SECS_PER_HOUR	3600
#define SECS_PER_MINUTE 60
#define MINS_PER_HOUR	60

#define USECS_PER_DAY	INT64CONST(86400000000)
#define USECS_PER_HOUR	INT64CONST(3600000000)
#define USECS_PER_MINUTE INT64CONST(60000000)
#define USECS_PER_SEC	INT64CONST(1000000)

/*
 * 我们允许从格林威治向两侧偏移最多15:59:59的数值时区。
 * 目前，实际使用中偏移量最奇怪的记录保持者是区域
 * Asia/Manila，在1844年为-15:56:08，以及America/Metlakatla，在1867年为+15:13:42。
 * 如果我们拒绝这样的值，我们将无法转储和
 * 恢复带有这些时区设置的旧timestamptz值。
 */
#define MAX_TZDISP_HOUR		15	/* 允许的最大小时部分 */
#define TZDISP_LIMIT		((MAX_TZDISP_HOUR + 1) * SECS_PER_HOUR)

/*
 * DT_NOBEGIN代表时间戳-infinity；DT_NOEND代表 +infinity
 */
#define DT_NOBEGIN		PG_INT64_MIN
#define DT_NOEND		PG_INT64_MAX

#define TIMESTAMP_NOBEGIN(j)	\
	do {(j) = DT_NOBEGIN;} while (0)

#define TIMESTAMP_IS_NOBEGIN(j) ((j) == DT_NOBEGIN)

#define TIMESTAMP_NOEND(j)		\
	do {(j) = DT_NOEND;} while (0)

#define TIMESTAMP_IS_NOEND(j)	((j) == DT_NOEND)

#define TIMESTAMP_NOT_FINITE(j) (TIMESTAMP_IS_NOBEGIN(j) || TIMESTAMP_IS_NOEND(j))


/*
 * 儒略日期支持。
 *
 * date2j()和j2date()名义上处理儒略日期范围0..INT_MAX，
 * 或4714-11-24 BC到5874898-06-03 AD。实际上，date2j()将工作并
 * 为4714-11-24 BC之前的日期提供正确的负儒略日期。
 * 我们依赖它这样做直到4714-11-01 BC。允许至少一天的
 * 偏差是必要的，以便时间戳旋转不会生成在输入时
 * 被拒绝的日期。例如，'4714-11-24 00:00 GMT BC'是一个
 * 合法的timestamptz值，但在格林威治以东的时区，它将打印为
 * 4714-11-23 BC的某个下午；如果我们无法处理这样的
 * 日期，我们将会遇到转储/重新加载失败。因此，IS_VALID_JULIAN
 *的想法是接受略微宽泛的日期范围，而不是我们真正支持的，
 * 然后我们在IS_VALID_DATE或IS_VALID_TIMESTAMP中应用
 * 精确的检查，若有时间区转换。在节省几个周期的情况下，
 * 我们可以确保IS_VALID_JULIAN仅检查到月份边界，
 * 因为在这个方案中，其确切的分界线并不是很关键。
 *
 * JULIAN_MINYEAR正确定义为-4713，而不是-4714；它被定义为
 * 便于与tm_year值进行比较，在其中我们遵循的约定
 * 是tm_year <= 0表示绝对值(tm_year)+1 BC。
 */

#define JULIAN_MINYEAR (-4713)
#define JULIAN_MINMONTH (11)
#define JULIAN_MINDAY (24)
#define JULIAN_MAXYEAR (5874898)
#define JULIAN_MAXMONTH (6)
#define JULIAN_MAXDAY (3)

#define IS_VALID_JULIAN(y,m,d) \
	(((y) > JULIAN_MINYEAR || \
	  ((y) == JULIAN_MINYEAR && ((m) >= JULIAN_MINMONTH))) && \
	 ((y) < JULIAN_MAXYEAR || \
	  ((y) == JULIAN_MAXYEAR && ((m) < JULIAN_MAXMONTH))))

/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */
#define UNIX_EPOCH_JDATE		2440588 /* == date2j(1970, 1, 1) */
#define POSTGRES_EPOCH_JDATE	2451545 /* == date2j(2000, 1, 1) */

/*
 * 日期和时间戳的范围限制。
 *
 * 我们传统上允许 Julian 第零天作为有效的日期时间值，
 * 因此这是日期和时间戳的下限。
 *
 * 日期的上限是 5874897-12-31，这稍微低于 Julian 日期代码所能允许的。
 * 对于时间戳，上限是 294276-12-31。int64 溢出限制会在几天后；
 * 再次 leaving some slop 避免了对边缘情况溢出的担忧，并提供了
 * 一个更简单的用户可见定义。
 */

/* 首个允许的日期，以及 Julian 日期格式中的首个不允许的日期 */
#define DATETIME_MIN_JULIAN (0)
#define DATE_END_JULIAN (2147483494)	/* == date2j(JULIAN_MAXYEAR, 1, 1) */
#define TIMESTAMP_END_JULIAN (109203528)	/* == date2j(294277, 1, 1) */

/* 时间戳限制 */
#define MIN_TIMESTAMP	INT64CONST(-211813488000000000)
/* == (DATETIME_MIN_JULIAN - POSTGRES_EPOCH_JDATE) * USECS_PER_DAY */
#define END_TIMESTAMP	INT64CONST(9223371331200000000)
/* == (TIMESTAMP_END_JULIAN - POSTGRES_EPOCH_JDATE) * USECS_PER_DAY */

/* 范围检查一个日期（在 Postgres 中给出，而不是 Julian 编号） */
#define IS_VALID_DATE(d) \
	((DATETIME_MIN_JULIAN - POSTGRES_EPOCH_JDATE) <= (d) && \
	 (d) < (DATE_END_JULIAN - POSTGRES_EPOCH_JDATE))

/* 范围检查一个时间戳 */
#define IS_VALID_TIMESTAMP(t)  (MIN_TIMESTAMP <= (t) && (t) < END_TIMESTAMP)

#endif							/* DATATYPE_TIMESTAMP_H */
